package secondriver.springsubway.scaffold;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.StreamUtils;

/**
 * 
 * @author secondriver
 * @created 2015年4月18日 下午5:20:48
 */

/**
 * <p>
 * SQLFactoryBean for loading sql file from classpath , filesystem, url and so
 * on.
 * </p>
 * <p>
 * Example:
 * </p>
 * 
 * <pre>
 * &lt;bean id="sqlFactoryBean" class="secondriver.springsubway.scaffold.SQLFactoryBean" init-method="init"&gt;
 *  &lt;property name="sqlMap"&gt
 *   &lt;map key-type="java.lang.String" value-type="java.lang.String"&gt;
 *   &lt;entry key="sql1" value="select * from sql1" /&gt;
 * 	  &lt;entry key="sql2" value="select * from sql1" /&gt;
 * 	  &lt;entry key="save_post.lua" value="select * from sql1" /&gt;
 *   &lt;/map&gt;
 *  &lt;/property&gt;
 *  &lt;property name="sqlMapResource"&gt;
 *   &lt;map key-type="java.lang.String" value-type="org.springframework.core.io.Resource" &gt;
 * 	 &lt;entry key="schema-sql" value="classpath:script/database-schema.sql" /&gt;
 *   &lt;entry key="lua.script" value="classpath:script/save_post.lua" /&gt;
 *   &lt;/map&gt;
 *  &lt;/property&gt;
 *  &lt;property name="sqlResources" value="classpath:script/*.lua" /&gt; 
 * &lt;/bean&gt;
 * </pre>
 * <p>
 * Notice:SQL缓存的优先级从高到低依次是:sqlMapResource, sqlResources相同Key时后者无法覆盖前者的Key
 * </p>
 */
public final class SQLFactoryBean{

	private Map<String, String> sqlMap;

	private Map<String, Resource> sqlMapResource;

	private Resource[] sqlResources;

	private Map<String, String> procedureNames;

	private final ConcurrentHashMap<String, String> cacheSQL = new ConcurrentHashMap<String, String>(
	        32);

	public void init() {

		cacheSQL.clear();

		if (null != sqlMapResource && sqlMapResource.size() > 0) {
			for (Map.Entry<String, Resource> m : sqlMapResource.entrySet()) {
				Resource res = m.getValue();
				cacheSQL.putIfAbsent(m.getKey(), readResourceToString(res));
			}
		}

		if (null != sqlResources && sqlResources.length > 0) {
			for (Resource res : sqlResources) {
				cacheSQL.putIfAbsent(res.getFilename(),
				        readResourceToString(res));
			}
		}
	}

	public String getSql(String name) {
		Assert.notNull(name, "Sql key must not null.");

		if (sqlMap.containsKey(name)) {
			return sqlMap.get(name);
		}
		if (cacheSQL.containsKey(name)) {
			return cacheSQL.get(name);
		} else {
			return "";
		}
	}

	public String getProcedure(String name) {
		Assert.notNull(name, "Procedure key must not null.");
		if (procedureNames.containsKey(name)) {
			return procedureNames.get(name);
		} else {
			return "";
		}
	}

	public void cacheSql(String name, String sql) {
		cacheSQL.put(name, sql);
	}

	public void cacheSql(String name, String oldSql, String newSql) {
		cacheSQL.replace(name, oldSql, newSql);
	}

	public void removeSql(String name) {
		cacheSQL.remove(name);
	}

	private String readResourceToString(Resource resource) {
		try {
			if (!resource.exists()) {
				throw new FileNotFoundException(resource.getFilename()
				        + " not found.");
			}
			return StreamUtils.copyToString(resource.getInputStream(),
			        Charset.forName("UTF-8"));
		} catch (IOException e) {
			throw new IllegalArgumentException(resource.getFilename()
			        + " convert to string failed.");
		}
	}

	public Map<String, String> getSqlMap() {
		return sqlMap;
	}
	
    public void setSqlMap(Map<String, String> sqlMap) {
    	this.sqlMap = sqlMap;
    }

	public void setSqlMapResource(Map<String, Resource> sqlMapResource) {
		this.sqlMapResource = sqlMapResource;
	}

	public void setSqlResources(Resource[] sqlResources) {
		this.sqlResources = sqlResources;
	}

	public void setProcedureNames(Map<String, String> procedureNames) {
		this.procedureNames = procedureNames;
	}

	public Map<String, Resource> getSqlMapResource() {
		return sqlMapResource;
	}

	public Resource[] getSqlResources() {
		return sqlResources;
	}

	public void printCacheSql() {
		int count = 0;
		for (Map.Entry<String, String> m : cacheSQL.entrySet()) {
			count++;
			System.out.println(count + "==========");
			System.out.println("Key:" + m.getKey());
			System.out.println("Value:\n" + m.getValue());
		}
	}
}
